package com.ldl.handle.spring.framework.context;

import com.ldl.handle.spring.framework.annotation.LdlAutowired;
import com.ldl.handle.spring.framework.annotation.LdlController;
import com.ldl.handle.spring.framework.annotation.LdlService;
import com.ldl.handle.spring.framework.beans.LdlBeanWrapper;
import com.ldl.handle.spring.framework.beans.config.LdlBeanDefinition;
import com.ldl.handle.spring.framework.beans.support.LdlBeanDefinitionReader;
import com.ldl.handle.spring.framework.beans.support.LdlDefaultListableBeanFactory;
import com.ldl.handle.spring.framework.core.LdlBeanFactory;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author ldl  想过夏天的雪人
 * @Date 2022-10-13 11:23
 */
public class LdlApplicationContext implements LdlBeanFactory {
    //标记器
    private Set<String> singletonCurrentlyCreation = new HashSet<String>();
    //一级缓存
    private Map<String, Object> singletonObjects = new HashMap<String, Object>();
    //二级缓存
    private Map<String, Object> earlySingletonObjects = new HashMap<String, Object>();
    // 三级缓存
    private Map<String, LdlBeanWrapper> factoryBeanInstanceCache = new HashMap<String, LdlBeanWrapper>();

    private LdlDefaultListableBeanFactory registry = new LdlDefaultListableBeanFactory();

    private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();
    //配置解析器
    private LdlBeanDefinitionReader reader;

    public LdlApplicationContext(String... configLocations) {
        //1. 加载配置文件
        reader = new LdlBeanDefinitionReader(configLocations);
        try {

            //2. 将所有的配置信息封装厂 beandefinition 对象
            List<LdlBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

            //3. 将所有的配置缓存起来
            this.registry.doRegistBeanDefinition(beanDefinitions);

            //4. 加载非延迟加载的所有bean
            doLoadInstance();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doLoadInstance() {
        for (Map.Entry<String, LdlBeanDefinition> entry : this.registry.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            if (!entry.getValue().isLazyInit()) {
                getBean(beanName);
            }
        }
    }

    @Override
    public Object getBean(Class beanClass) {
        return getBean(beanClass.getName());
    }

    @Override
    public Object getBean(String beanName) {
    /*    if (this.factoryBeanInstanceCache.containsKey(beanName)) {
            return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();
        }*/
        //1.拿到Beandefinition配置信息
        LdlBeanDefinition beanDefinition = registry.beanDefinitionMap.get(beanName);

        //
        Object singleton = getSingleton(beanName, beanDefinition);
        if (singleton != null) {
            return singleton;
        }
        //标记正在创建的bean
        if (!singletonCurrentlyCreation.contains(beanName)) {
            singletonCurrentlyCreation.add(beanName);
        }
        //通过反射实例化对象
        Object instance = instantiateBean(beanName, beanDefinition);
        // 将对象存入一级缓存
        this.singletonObjects.put(beanName, instance);

        //3.将返回的bean对象封装成beanwrapper
        LdlBeanWrapper beanWrapper = new LdlBeanWrapper(instance);
        //4. 执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);
        //5.保存到ioc 容器
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);

        return beanWrapper.getWrapperInstance();
    }

    private Object getSingleton(String beanName, LdlBeanDefinition beanDefinition) {
        //取一级缓存，
        Object bean = singletonObjects.get(beanName);
        //如果一级缓存没有，但是标识有，说明有循环依赖
        if (null == bean && singletonCurrentlyCreation.contains(beanName)) {
            bean = earlySingletonObjects.get(beanName);
            //二级缓存没有，就去三级缓存里面去拿
            if (null == bean) {
                Object object = instantiateBean(beanName, beanDefinition);
                //将创建出来的bean 放入二级缓存，避免重复创建
                earlySingletonObjects.put(beanName, object);
            }
        }
        return bean;
    }

    private void populateBean(String beanName, LdlBeanDefinition beanDefinition, LdlBeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrapperInstance();
        Class<?> clazz = beanWrapper.getWrapperClass();

        if (!(clazz.isAnnotationPresent(LdlController.class) || clazz.isAnnotationPresent(LdlService.class))) {
            return;
        }
        //忽略字段的修饰符，不管你是 private / protected / public / default
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.isAnnotationPresent(LdlAutowired.class)) {
                continue;
            }

            LdlAutowired autowired = field.getAnnotation(LdlAutowired.class);
            String autiwuredBeanName = autowired.value().trim();
            if ("".equals(autiwuredBeanName)) {
                autiwuredBeanName = field.getType().getName();
            }

            //代码在反射面前，那就是裸奔
            //强制访问，强吻
            field.setAccessible(true);

            try {
                field.set(instance, getBean(autiwuredBeanName));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }

    private Object instantiateBean(String beanName, LdlBeanDefinition beanDefinition) {
        if (beanDefinition.isSingleton() && this.factoryBeanObjectCache.containsKey(beanName)) {
            return this.factoryBeanObjectCache.get(beanName);
        }
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {

            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();

            //todo 如果是代理对象。。 开始aop 逻辑
            this.factoryBeanObjectCache.put(beanName, instance);

            this.factoryBeanObjectCache.put(clazz.getName(), instance);

            for (Class<?> i : clazz.getInterfaces()) {
                this.factoryBeanObjectCache.put(i.getName(), instance);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    public int getBeanDefinitionCount() {
        return this.registry.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.registry.beanDefinitionMap.keySet().toArray(new String[0]);
    }
}
